/**
 * @file spi_op.c
 * @author windowsair
 * @brief 使用SPI进行通用传输操作
 * @change: 2020-11-25 第一版
 *          2021-2-11  支持SWD序列
 *          2021-3-10  支持3线SPI
 *          2022-9-15  支持ESP32C3
 *          2024-6-9   修复DAP_SPI_WriteBits问题
 * @version 0.5
 * @date 2024-6-9
 *
 * @copyright MIT License
 *
 */
// 包含SDK配置文件
#include "sdkconfig.h"

// 包含标准输入输出库，用于输入输出操作
#include <stdio.h>
// 包含字符串处理库，用于字符串操作
#include <string.h>
// 包含布尔类型定义，用于使用布尔值
#include <stdbool.h>

// 包含主配置文件

// 包含CMSIS编译器相关头文件
#include "cmsis_compiler.h"
#include "dap_spi_config.h"
// 包含SPI操作头文件
#include "spi_op.h"
// 包含SPI切换头文件
#include "spi_switch.h"
// 包含spi通用头文件
#include "soc/io_mux_reg.h"
#include "soc/spi_struct.h"
#include "soc/spi_reg.h"
#include "hal/gpio_ll.h"

#define DAP_SPI GPSPI2

// 定义设置MOSI位长度的宏
#define SET_MOSI_BIT_LEN(x) DAP_SPI.ms_dlen.ms_data_bitlen = x
// 定义设置MISO位长度的宏
#define SET_MISO_BIT_LEN(x) DAP_SPI.ms_dlen.ms_data_bitlen = x
// 定义启动SPI传输并等待完成的宏
#define START_AND_WAIT_SPI_TRANSMISSION_DONE() \
    do {                                       \
        DAP_SPI.cmd.update = 1; /* 更新配置 */ \
        while (DAP_SPI.cmd.update)             \
            continue;                          \
        DAP_SPI.cmd.usr = 1; /* 启动传输 */    \
        while (DAP_SPI.cmd.usr)                \
            continue;                          \
    } while (0)

/**
 * @brief 计算整数除法并向上取整
 *
 * @param A 被除数
 * @param B 除数
 * @return int 结果
 */
// 定义一个内联函数，用于计算整数除法并向上取整
__STATIC_FORCEINLINE int div_round_up(int A, int B)
{
    // 计算并返回结果
    return (A + B - 1) / B;
}

/**
 * @brief 写数据位(LSB & 小端格式)
 *        注意: 无检查。指针必须有效。
 * @param count 要写入的位数(<=64位，无长度检查)
 * @param buf 数据缓冲区
 */
// 定义写数据位的函数
void DAP_SPI_WriteBits(const uint8_t count, const uint8_t *buf)
{
    // 定义一个32位无符号整数数组，用于存储数据
    uint32_t data[16];
    // 定义需要的字节数和循环计数器
    int nbytes, i;

    // 禁用命令阶段
    DAP_SPI.user.usr_command = 0;
    // 禁用地址阶段
    DAP_SPI.user.usr_addr = 0;

    // 启用MOSI
    DAP_SPI.user.usr_mosi = 1;
    // 禁用MISO
    DAP_SPI.user.usr_miso = 0;
    // 设置传输位数(寄存器值为位数-1)
    SET_MOSI_BIT_LEN(count - 1);

    // 计算需要的字节数
    nbytes = div_round_up(count, 8);
    // 将数据从buf拷贝到data数组
    memcpy(data, buf, nbytes);

    // 将数据写入SPI缓冲区
    for (i = 0; i < nbytes; i++) {
        DAP_SPI.data_buf[i] = data[i];
    }

    // 启动传输并等待完成
    START_AND_WAIT_SPI_TRANSMISSION_DONE();
}

/**
 * @brief 读数据位(LSB & 小端格式)
 *        注意: 无检查。指针必须有效。
 * @param count 要读取的位数(<=64位，无长度检查)
 * @param buf 数据缓冲区
 */
// 定义读数据位的函数
void DAP_SPI_ReadBits(const uint8_t count, uint8_t *buf)
{
    // 定义循环计数器
    int i;
    // 定义一个32位无符号整数数组，用于存储数据
    uint32_t data_buf[2];
    // 定义一个指向无符号字符的指针，用于操作数据
    uint8_t *pData = (uint8_t *)data_buf;

    // 禁用MOSI
    DAP_SPI.user.usr_mosi = 0;
    // 启用MISO
    DAP_SPI.user.usr_miso = 1;

#if (USE_SPI_SIO == 1)
    // 启用SIO模式(单线双向)
    DAP_SPI.user.sio = true;
#endif

    // 设置接收位数
    SET_MISO_BIT_LEN(count - 1U);

    // 启动传输并等待完成
    START_AND_WAIT_SPI_TRANSMISSION_DONE();

#if (USE_SPI_SIO == 1)
    // 禁用SIO模式
    DAP_SPI.user.sio = false;
#endif

    // 读取数据缓冲区
    data_buf[0] = DAP_SPI.data_buf[0];
    data_buf[1] = DAP_SPI.data_buf[1];

    // 拷贝数据到输出缓冲区
    for (i = 0; i < div_round_up(count, 8); i++) {
        buf[i] = pData[i];
    }
    // 对最后一个字节应用掩码(处理不足8位的情况)
    buf[i - 1] = buf[i - 1] & ((2 >> (count % 8)) - 1);
}
// ESP32C3/ESP32S3的实现(使用命令阶段发送头数据)
// 定义发送数据包头的内联函数
__FORCEINLINE void DAP_SPI_Send_Header(const uint8_t packetHeaderData, uint8_t *ack, uint8_t TrnAfterACK)
{
    // 定义一个32位无符号整数，用于存储数据
    uint32_t dataBuf;

    // 禁用MOSI数据阶段
    DAP_SPI.user.usr_mosi = 0;
    // 启用命令阶段
    DAP_SPI.user.usr_command = 1;
    // 启用MISO接收
    DAP_SPI.user.usr_miso = 1;

    // 设置命令阶段: 8位头数据 + 1位Trn
    DAP_SPI.user2.usr_command_bitlen = 8U + 1U - 1U;
    DAP_SPI.user2.usr_command_value = packetHeaderData;

#if (USE_SPI_SIO == 1)
    // 启用SIO模式
    DAP_SPI.user.sio = true;
#endif

    // 设置接收位数: 3位ACK + TrnAfterACK位
    SET_MISO_BIT_LEN(3U + TrnAfterACK - 1U);

    // 启动传输并等待完成
    START_AND_WAIT_SPI_TRANSMISSION_DONE();

#if (USE_SPI_SIO == 1)
    // 禁用SIO模式
    DAP_SPI.user.sio = false;
#endif

    // 禁用命令阶段
    DAP_SPI.user.usr_command = 0;

    // 从接收数据中提取ACK位(3位)
    dataBuf = DAP_SPI.data_buf[0];
    
    *ack = dataBuf & 0b111;
}

/**
 * @brief 步骤2: 读取数据(所有平台通用)
 *
 * @param resData 从目标设备返回的32位数据
 * @param resParity 从目标设备返回的奇偶校验位
 */
// 定义读取数据的内联函数
__FORCEINLINE void DAP_SPI_Read_Data(uint32_t *resData, uint8_t *resParity)
{
    // 定义一个64位无符号整数，用于存储数据
    volatile uint64_t dataBuf;
    // 定义一个指向32位无符号整数的指针，用于操作数据
    uint32_t *pU32Data = (uint32_t *)&dataBuf;

    // 禁用MOSI
    DAP_SPI.user.usr_mosi = 0;
    // 启用MISO
    DAP_SPI.user.usr_miso = 1;

#if (USE_SPI_SIO == 1)
    // 启用SIO模式
    DAP_SPI.user.sio = true;
#endif

    // 设置接收位数: 1位Trn + 32位数据 + 1位奇偶校验
    SET_MISO_BIT_LEN(1U + 32U + 1U - 1U);

    // 启动传输并等待完成
    START_AND_WAIT_SPI_TRANSMISSION_DONE();

#if (USE_SPI_SIO == 1)
    // 禁用SIO模式
    DAP_SPI.user.sio = false;
#endif

    // 读取接收到的数据
    pU32Data[0] = DAP_SPI.data_buf[0];
    pU32Data[1] = DAP_SPI.data_buf[1];

    // 提取32位数据和奇偶校验位
    *resData = (dataBuf >> 0U) & 0xFFFFFFFFU;  // 32位响应数据
    *resParity = (dataBuf >> (0U + 32U)) & 1U; // 1位奇偶校验
}

// ESP32C3/ESP32S3的实现(需要额外发送1位以避免问题)
// 定义写入数据的内联函数
__FORCEINLINE void DAP_SPI_Write_Data(uint32_t data, uint8_t parity)
{
    // 启用MOSI
    DAP_SPI.user.usr_mosi = 1;
    // 禁用MISO
    DAP_SPI.user.usr_miso = 0;

    // ESP32C3不能正确发送33位数据，需要额外发送1位
    SET_MOSI_BIT_LEN(32U + 1U + 1U - 1U);
    DAP_SPI.data_buf[0] = data;
    DAP_SPI.data_buf[1] = parity == 0 ? 0b00 : 0b01; // 奇偶校验位+额外位

    // 启动传输并等待完成
    START_AND_WAIT_SPI_TRANSMISSION_DONE();
}

// 以下是针对不同硬件平台的时钟周期生成实现
// 如果是ESP8266、ESP32或ESP32S3平台
/**
 * @brief 生成时钟周期(ESP8266/ESP32/ESP32S3实现)
 *
 * @param num 要生成的时钟周期数
 */
// 定义生成时钟周期的内联函数
__FORCEINLINE void DAP_SPI_Generate_Cycle(uint8_t num)
{
    // TODO: 生成单个时钟可能需要较长时间
    // 启用MOSI(发送0)
    DAP_SPI.user.usr_mosi = 1;
    // 禁用MISO
    DAP_SPI.user.usr_miso = 0;
    // 设置时钟周期数
    SET_MOSI_BIT_LEN(num - 1U);

    // 发送全0数据
    DAP_SPI.data_buf[0] = 0x00000000U;

    // 启动传输并等待完成
    START_AND_WAIT_SPI_TRANSMISSION_DONE();
}

// 如果是ESP32、ESP32C3或ESP32S3平台
/**
 * @brief 快速生成1个时钟周期
 *
 */
// 定义快速生成1个时钟周期的内联函数
__FORCEINLINE void DAP_SPI_Fast_Cycle()
{
    // 释放SPI控制器
    DAP_SPI_Release();
    // 重新获取SPI控制器(产生时钟边沿)
    DAP_SPI_Acquire();
}

/**
 * @brief 生成协议错误读取周期
 *
 */
// 定义生成协议错误读取周期的内联函数
__FORCEINLINE void DAP_SPI_Protocol_Error_Read()
{
    // 启用MOSI
    DAP_SPI.user.usr_mosi = 1;
    // 禁用MISO
    DAP_SPI.user.usr_miso = 0;

    // 发送32位忽略数据+1位
    SET_MOSI_BIT_LEN(32U + 1U - 1);

    // 写入全1数据(协议错误)
    DAP_SPI.data_buf[0] = 0xFFFFFFFFU;
    DAP_SPI.data_buf[1] = 0xFFFFFFFFU;

    // 启动传输并等待完成
    START_AND_WAIT_SPI_TRANSMISSION_DONE();
}

/**
 * @brief 生成协议错误写入周期
 *
 */
// 定义生成协议错误写入周期的内联函数
__FORCEINLINE void DAP_SPI_Protocol_Error_Write()
{
    // 启用MOSI
    DAP_SPI.user.usr_mosi = 1;
    // 禁用MISO
    DAP_SPI.user.usr_miso = 0;

    // 发送1位Trn+32位忽略数据+1位
    SET_MOSI_BIT_LEN(1U + 32U + 1U - 1);

    // 写入全1数据(协议错误)
    DAP_SPI.data_buf[0] = 0xFFFFFFFFU;
    DAP_SPI.data_buf[1] = 0xFFFFFFFFU;

    // 启动传输并等待完成
    START_AND_WAIT_SPI_TRANSMISSION_DONE();
}